import userEvent from "@testing-library/user-event";

import { fireEvent, render, screen, within } from "__support__/ui";

import {
  ChartSettingSegmentsEditor,
  type Segment,
} from "./ChartSettingSegmentsEditor";

const createMockSegment = (opts?: Partial<Segment>): Segment => {
  return { label: "", min: 0, max: 100, color: "red", ...opts };
};

const DEFAULT_VALUE = [
  createMockSegment({ label: "bad" }),
  createMockSegment({ label: "good", min: 100, max: 200, color: "green" }),
];

const setup = () => {
  const onChange = jest.fn();
  render(
    <ChartSettingSegmentsEditor value={DEFAULT_VALUE} onChange={onChange} />,
  );
  return { onChange };
};

it("Should render a segment editor", () => {
  setup();

  // Add a row for the header
  expect(screen.getAllByRole("row")).toHaveLength(3);

  const firstRow = screen.getAllByRole("row").at(1) as HTMLElement;

  expect(within(firstRow).getByPlaceholderText(/Optional/)).toHaveValue("bad");
  expect(within(firstRow).getByPlaceholderText(/Min/)).toHaveValue("0");
  expect(within(firstRow).getByPlaceholderText(/Max/)).toHaveValue("100");
});

it("Should pass back a new array of segments on change", async () => {
  const { onChange } = setup();

  const min = await screen.findByDisplayValue("0");

  await userEvent.clear(min);
  await userEvent.type(min, "20");
  fireEvent.blur(min);

  expect(onChange).toHaveBeenCalledWith(
    expect.arrayContaining([
      expect.objectContaining({ ...DEFAULT_VALUE[0], min: 20 }),
      // Need to use objectContaining here to account for the 'key' values that are added
      expect.objectContaining(DEFAULT_VALUE[1]),
    ]),
  );
});

it("Should allow you to remove a segment", async () => {
  const { onChange } = setup();

  await userEvent.click(
    (await screen.findAllByRole("img", { name: /trash/ })).at(0) as HTMLElement,
  );

  expect(onChange).toHaveBeenCalledWith([
    // Need to use objectContaining here to account for the 'key' values that are added
    expect.objectContaining(DEFAULT_VALUE[1]),
  ]);
});

it("Should allow you to add a new segment with apropriate defaults", async () => {
  const { onChange } = setup();

  await userEvent.click(
    await screen.findByRole("button", { name: /Add a range/ }),
  );

  expect(onChange).toHaveBeenCalledWith([
    // Need to use objectContaining here to account for the 'key' values that are added
    expect.objectContaining(DEFAULT_VALUE[0]),
    expect.objectContaining(DEFAULT_VALUE[1]),
    expect.objectContaining({
      min: 200,
      max: 400,
      color: expect.anything(),
    }),
  ]);
});
